package me.xADudex.RandomLocation;

import org.bukkit.block.Chest;
import org.bukkit.scheduler.*;
import org.bukkit.plugin.*;
import com.palmergames.bukkit.towny.object.*;
import com.massivecraft.massivecore.ps.*;
import com.massivecraft.factions.entity.*;
import org.bukkit.entity.*;
import org.bukkit.*;
import org.bukkit.material.*;
import org.bukkit.block.*;
import me.ryanhamshire.GriefPrevention.*;
import com.sk89q.worldguard.bukkit.*;
import com.sk89q.worldguard.protection.regions.*;
import com.wimbli.WorldBorder.*;
import java.util.*;
import org.bukkit.inventory.*;

public class Zone
{
    private static Main pl;
    private static Integer[] air;
    private static Integer[] damager;
    private String name;
    private String perm;
    private boolean rangeEnable;
    private int range;
    private RangeFrom rangeFrom;
    private int rangeX;
    private int rangeY;
    private int rangeZ;
    private String rangeWorld;
    private boolean keepDirection;
    private int checks;
    private SpawnOn spawnOn;
    private BlockData[] spawnOnBlocks;
    private BlockData[] topBlocks;
    private int cmdCooldown;
    private int signsCooldown;
    private int portalCooldown;
    private int xmax;
    private int ymax;
    private int zmax;
    private int xmin;
    private int ymin;
    private int zmin;
    private int x1;
    private int y1;
    private int z1;
    private int x2;
    private int y2;
    private int z2;
    private HashMap<Integer, ItemStack> items;
    private boolean kit;
    private ZoneType type;
    private String world;
    private int invulnerability;
    HashMap<Location, ChunkSnapshot> locs;
    private Random ran;
    private String portalDest;
    private int maxLocs;
    private static int chunksPerCheck;
    private static int blocksPerChunk;
    private static int chunksPerRandomChunLoad;
    
    static {
        Zone.air = new Integer[] { 0, 6, 27, 28, 31, 32, 37, 38, 39, 40, 50, 55, 59, 63, 64, 65, 66, 68, 69, 71, 75, 76, 78, 77, 83, 93, 94, 96, 104, 105, 106, 111, 115, 141, 142, 143, 157, 171, 175 };
        Zone.damager = new Integer[] { 8, 9, 10, 11, 30, 51, 70, 72, 81, 90, 119, 132 };
        Zone.chunksPerCheck = 1;
        Zone.blocksPerChunk = 256;
        Zone.chunksPerRandomChunLoad = 50;
    }
    
    public Zone(final String name) {
        this.perm = "none";
        this.rangeEnable = false;
        this.range = 20;
        this.rangeFrom = RangeFrom.ZONE;
        this.rangeX = 0;
        this.rangeY = 0;
        this.rangeZ = 0;
        this.rangeWorld = "world";
        this.keepDirection = true;
        this.checks = 50;
        this.spawnOn = SpawnOn.GROUND;
        this.spawnOnBlocks = new BlockData[0];
        this.topBlocks = new BlockData[] { new BlockData(18) };
        this.cmdCooldown = 120;
        this.signsCooldown = 0;
        this.portalCooldown = 0;
        this.xmax = 0;
        this.ymax = 0;
        this.zmax = 0;
        this.xmin = 0;
        this.ymin = 0;
        this.zmin = 0;
        this.x1 = 0;
        this.y1 = 0;
        this.z1 = 0;
        this.x2 = 0;
        this.y2 = 0;
        this.z2 = 0;
        this.items = new HashMap<Integer, ItemStack>();
        this.kit = false;
        this.type = ZoneType.TELEPORT;
        this.world = Bukkit.getWorlds().get(0).getName();
        this.invulnerability = 10;
        this.locs = new HashMap<Location, ChunkSnapshot>();
        this.ran = new Random();
        this.portalDest = "none";
        this.maxLocs = 500;
        this.name = name;
    }
    
    Location getLocation(final ChunkSnapshot c, final ChunkCoord cc, final int y) {
        return new Location(this.getWorld(), (double)(c.getX() * 16 + cc.getX()), (double)y, (double)(c.getZ() * 16 + cc.getZ()));
    }
    
    public static void setPlugin(final Main main) {
        Zone.pl = main;
    }
    
    ChunkSnapshot getRandomChunkSnapshot() {
        final World w = Bukkit.getWorld(this.world);
        if (w == null) {
            return null;
        }
        final int cxmax = (int)((this.xmax <= 0) ? (Math.floor(this.xmax / 16.0) + 1.0) : Math.ceil(this.xmax / 16.0)) - 1;
        final int czmax = (int)((this.zmax <= 0) ? (Math.floor(this.zmax / 16.0) + 1.0) : Math.ceil(this.zmax / 16.0)) - 1;
        final int cxmin = (int)((this.xmin <= 0) ? (Math.floor(this.xmin / 16.0) + 1.0) : Math.ceil(this.xmin / 16.0)) - 1;
        final int czmin = (int)((this.zmin <= 0) ? (Math.floor(this.zmin / 16.0) + 1.0) : Math.ceil(this.zmin / 16.0)) - 1;
        for (int i = 0; i < Zone.chunksPerRandomChunLoad; ++i) {
            final int cx = this.ran.nextInt(cxmax - cxmin + 1) + cxmin;
            final int cz = this.ran.nextInt(czmax - czmin + 1) + czmin;
            if (this.checkChunk(cx, cz)) {
                return w.getChunkAt(cx, cz).getChunkSnapshot(false, false, false);
            }
        }
        return null;
    }
    
    public boolean loadLocation(final ChunkSnapshot c) {
        if (c == null) {
            return false;
        }
        while (this.locs.size() >= this.maxLocs) {
            this.locs.remove(this.locs.keySet().iterator().next());
        }
        if (this.spawnOn.equals(SpawnOn.AIR)) {
            final int x = this.ran.nextInt(this.xmax - this.xmin + 1) + this.xmin;
            final int z = this.ran.nextInt(this.zmax - this.zmin + 1) + this.zmin;
            final World w = Bukkit.getWorld(this.world);
            if (w != null) {
                final Location loc = new Location(w, (double)x, 300.0, (double)z);
                this.addLocation(loc, c);
                return true;
            }
        }
        else if (this.spawnOn.equals(SpawnOn.TOP) || this.spawnOn.equals(SpawnOn.COMBINED)) {
            final ArrayList<ChunkCoord> list = new ArrayList<ChunkCoord>();
            byte maxx = (byte)((c.getX() * 16 + 15 > this.xmax) ? (this.xmax % 16) : 15);
            if (maxx < 0) {
                maxx += 16;
            }
            byte minx = (byte)((c.getX() * 16 < this.xmin) ? (this.xmin % 16) : 0);
            if (minx < 0) {
                minx += 16;
            }
            byte maxz = (byte)((c.getZ() * 16 + 15 > this.zmax) ? (this.zmax % 16) : 15);
            if (maxz < 0) {
                maxz += 16;
            }
            byte minz = (byte)((c.getZ() * 16 < this.zmin) ? (this.zmin % 16) : 0);
            if (minz < 0) {
                minz += 16;
            }
            for (byte x2 = minx; x2 <= maxx; ++x2) {
                for (byte z2 = minz; z2 <= maxz; ++z2) {
                    list.add(new ChunkCoord(x2, z2));
                }
            }
            Collections.shuffle(list);
            int count = 0;
            for (final ChunkCoord cc : list) {
                if (count >= Zone.blocksPerChunk) {
                    break;
                }
                final int y = this.getHigestBlock(c, cc);
                if (y >= this.ymin && y <= this.ymax && this.checkLocation(c, cc, y)) {
                    this.addLocation(this.getLocation(c, cc, y), c);
                    return true;
                }
                ++count;
            }
        }
        else if (this.spawnOn.equals(SpawnOn.GROUND)) {
            final ArrayList<ChunkCoord> list = new ArrayList<ChunkCoord>();
            byte maxx = (byte)((c.getX() * 16 + 15 > this.xmax) ? (this.xmax % 16) : 15);
            if (maxx < 0) {
                maxx += 16;
            }
            byte minx = (byte)((c.getX() * 16 < this.xmin) ? (this.xmin % 16) : 0);
            if (minx < 0) {
                minx += 16;
            }
            byte maxz = (byte)((c.getZ() * 16 + 15 > this.zmax) ? (this.zmax % 16) : 15);
            if (maxz < 0) {
                maxz += 16;
            }
            byte minz = (byte)((c.getZ() * 16 < this.zmin) ? (this.zmin % 16) : 0);
            if (minz < 0) {
                minz += 16;
            }
            for (byte x2 = minx; x2 <= maxx; ++x2) {
                for (byte z2 = minz; z2 <= maxz; ++z2) {
                    list.add(new ChunkCoord(x2, z2));
                }
            }
            Collections.shuffle(list);
            int count = 0;
            for (final ChunkCoord cc : list) {
                if (count >= Zone.blocksPerChunk) {
                    break;
                }
                final int y = this.getGroundLoc(c, cc);
                if (y != -1) {
                    final Location loc2 = this.getLocation(c, cc, y);
                    this.addLocation(loc2, c);
                    return true;
                }
                ++count;
            }
        }
        return false;
    }
    
    public boolean loadLocation() {
        while (this.locs.size() >= this.maxLocs) {
            this.locs.remove(this.locs.keySet().iterator().next());
        }
        if (this.spawnOn.equals(SpawnOn.AIR)) {
            final int x = this.ran.nextInt(this.xmax - this.xmin + 1) + this.xmin;
            final int z = this.ran.nextInt(this.zmax - this.zmin + 1) + this.zmin;
            final World w = Bukkit.getWorld(this.world);
            if (w != null) {
                final Location loc = new Location(w, (double)x, 300.0, (double)z);
                final Chunk c = loc.getChunk();
                if (this.checkChunk(c)) {
                    this.addLocation(loc, c.getChunkSnapshot(false, false, false));
                    return true;
                }
            }
        }
        else if (this.spawnOn.equals(SpawnOn.TOP) || this.spawnOn.equals(SpawnOn.COMBINED)) {
            final World w2 = Bukkit.getWorld(this.world);
            if (w2 == null) {
                return false;
            }
            final ArrayList<ChunkCoord> chunks = new ArrayList<ChunkCoord>();
            final int cxmax = (int)((this.xmax < 0) ? (Math.floor(this.xmax / 16.0) + 1.0) : Math.ceil(this.xmax / 16.0)) - 1;
            final int czmax = (int)((this.zmax < 0) ? (Math.floor(this.zmax / 16.0) + 1.0) : Math.ceil(this.zmax / 16.0)) - 1;
            final int cxmin = (int)((this.xmin < 0) ? (Math.floor(this.xmin / 16.0) + 1.0) : Math.ceil(this.xmin / 16.0)) - 1;
            final int czmin = (int)((this.zmin < 0) ? (Math.floor(this.zmin / 16.0) + 1.0) : Math.ceil(this.zmin / 16.0)) - 1;
            for (int chunkCheck = 0; chunkCheck < Zone.chunksPerCheck; ++chunkCheck) {
                final int cx = this.ran.nextInt(cxmax - cxmin + 1) + cxmin;
                final int cz = this.ran.nextInt(czmax - czmin + 1) + czmin;
                final ChunkCoord chunk = new ChunkCoord(cx, cz);
                if (!chunks.contains(chunk)) {
                    chunks.add(chunk);
                    final Chunk c2 = w2.getChunkAt(cx, cz);
                    if (this.checkChunk(c2)) {
                        final ArrayList<ChunkCoord> list = new ArrayList<ChunkCoord>();
                        byte maxx = (byte)((chunk.getX() * 16 + 15 > this.xmax) ? (this.xmax % 16) : 15);
                        if (maxx < 0) {
                            maxx += 16;
                        }
                        byte minx = (byte)((chunk.getX() * 16 < this.xmin) ? (this.xmin % 16) : 0);
                        if (minx < 0) {
                            minx += 16;
                        }
                        byte maxz = (byte)((chunk.getZ() * 16 + 15 > this.zmax) ? (this.zmax % 16) : 15);
                        if (maxz < 0) {
                            maxz += 16;
                        }
                        byte minz = (byte)((chunk.getZ() * 16 < this.zmin) ? (this.zmin % 16) : 0);
                        if (minz < 0) {
                            minz += 16;
                        }
                        for (byte x2 = minx; x2 <= maxx; ++x2) {
                            for (byte z2 = minz; z2 <= maxz; ++z2) {
                                list.add(new ChunkCoord(x2, z2));
                            }
                        }
                        Collections.shuffle(list);
                        int count = 0;
                        for (final ChunkCoord cc : list) {
                            if (count >= Zone.blocksPerChunk) {
                                break;
                            }
                            final Location loc2 = c2.getBlock(cc.getX(), 0, cc.getZ()).getLocation();
                            final int y = this.getHigestBlock(loc2);
                            if (y >= this.ymin && y <= this.ymax) {
                                loc2.setY((double)y);
                                if (this.checkLocation(loc2)) {
                                    this.addLocation(loc2, c2.getChunkSnapshot(false, false, false));
                                    return true;
                                }
                            }
                            ++count;
                        }
                    }
                }
            }
        }
        else if (this.spawnOn.equals(SpawnOn.GROUND)) {
            final World w2 = Bukkit.getWorld(this.world);
            if (w2 == null) {
                return false;
            }
            final ArrayList<ChunkCoord> chunks = new ArrayList<ChunkCoord>();
            final int cxmax = (int)((this.xmax < 0) ? (Math.floor(this.xmax / 16.0) + 1.0) : Math.ceil(this.xmax / 16.0)) - 1;
            final int czmax = (int)((this.zmax < 0) ? (Math.floor(this.zmax / 16.0) + 1.0) : Math.ceil(this.zmax / 16.0)) - 1;
            final int cxmin = (int)((this.xmin < 0) ? (Math.floor(this.xmin / 16.0) + 1.0) : Math.ceil(this.xmin / 16.0)) - 1;
            final int czmin = (int)((this.zmin < 0) ? (Math.floor(this.zmin / 16.0) + 1.0) : Math.ceil(this.zmin / 16.0)) - 1;
            for (int chunkCheck = 0; chunkCheck < Zone.chunksPerCheck; ++chunkCheck) {
                final int cx = this.ran.nextInt(cxmax - cxmin + 1) + cxmin;
                final int cz = this.ran.nextInt(czmax - czmin + 1) + czmin;
                final ChunkCoord chunk = new ChunkCoord(cx, cz);
                if (!chunks.contains(chunk)) {
                    chunks.add(chunk);
                    final Chunk c2 = w2.getChunkAt(cx, cz);
                    if (this.checkChunk(c2)) {
                        final ArrayList<ChunkCoord> list = new ArrayList<ChunkCoord>();
                        byte maxx = (byte)((chunk.getX() * 16 + 15 > this.xmax) ? (this.xmax % 16) : 15);
                        if (maxx < 0) {
                            maxx += 16;
                        }
                        byte minx = (byte)((chunk.getX() * 16 < this.xmin) ? (this.xmin % 16) : 0);
                        if (minx < 0) {
                            minx += 16;
                        }
                        byte maxz = (byte)((chunk.getZ() * 16 + 15 > this.zmax) ? (this.zmax % 16) : 15);
                        if (maxz < 0) {
                            maxz += 16;
                        }
                        byte minz = (byte)((chunk.getZ() * 16 < this.zmin) ? (this.zmin % 16) : 0);
                        if (minz < 0) {
                            minz += 16;
                        }
                        for (byte x2 = minx; x2 <= maxx; ++x2) {
                            for (byte z2 = minz; z2 <= maxz; ++z2) {
                                list.add(new ChunkCoord(x2, z2));
                            }
                        }
                        Collections.shuffle(list);
                        int count = 0;
                        for (final ChunkCoord cc : list) {
                            if (count >= Zone.blocksPerChunk) {
                                break;
                            }
                            Location loc2 = c2.getBlock(cc.getX(), 0, cc.getZ()).getLocation();
                            loc2 = this.getGroundLoc(loc2);
                            if (loc2 != null) {
                                this.addLocation(loc2, c2.getChunkSnapshot(false, false, false));
                                return true;
                            }
                            ++count;
                        }
                    }
                }
            }
        }
        return false;
    }
    
    void addLocation(final Location loc, final ChunkSnapshot c) {
        this.locs.put(loc, c);
        final BukkitRunnable loadChunk = new BukkitRunnable() {
            public void run() {
                final Chunk c = loc.getChunk();
                c.load(true);
            }
        };
        if (!Bukkit.isPrimaryThread()) {
            loadChunk.runTask((Plugin)Zone.pl);
        }
        else {
            loadChunk.run();
        }
    }
    
    public void setPos(final Location loc, final boolean first) {
        this.world = loc.getWorld().getName();
        if (first) {
            this.x1 = loc.getBlockX();
            this.y1 = loc.getBlockY();
            this.z1 = loc.getBlockZ();
            if (this.y1 > 255) {
                this.y1 = 255;
            }
            if (this.y1 < 0) {
                this.y1 = 0;
            }
        }
        else {
            this.x2 = loc.getBlockX();
            this.y2 = loc.getBlockY();
            this.z2 = loc.getBlockZ();
            if (this.y2 > 255) {
                this.y2 = 255;
            }
            if (this.y2 < 0) {
                this.y2 = 0;
            }
        }
        this.calculatePitch();
        this.locs.clear();
        Zone.pl.zoneManager.save(this);
    }
    
    private int getMax(final int a, final int b) {
        if (a < b) {
            return b;
        }
        return a;
    }
    
    private int getMin(final int a, final int b) {
        if (a < b) {
            return a;
        }
        return b;
    }
    
    public String getName() {
        return this.name;
    }
    
    boolean checkChunk(final Chunk c) {
        return this.checkChunk(c.getX(), c.getZ());
    }
    
    boolean checkChunk(final int x, final int z) {
        if (Zone.pl.useTownyHook()) {
            try {
                final WorldCoord coord = new WorldCoord(this.world, x, z);
                try {
                    if (!Zone.pl.ignoreTowns.contains(coord.getTownBlock().getTown().getName().toLowerCase())) {
                        return false;
                    }
                }
                catch (Exception ex) {}
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("Towny");
                Zone.pl.townyHook = false;
            }
        }
        if (Zone.pl.useFactionHook()) {
            final Faction faction = BoardColl.get().getFactionAt(PS.valueOf(this.world, x, z));
            if (!faction.isNone() && !Zone.pl.ignoreFactions.contains(faction.getName().toLowerCase())) {
                return false;
            }
        }
        return true;
    }
    
    private Location getGroundLoc(Location loc) {
        final ArrayList<Location> list = new ArrayList<Location>();
        loc = loc.clone();
        for (int y = this.ymin; y <= this.ymax; ++y) {
            loc.setY((double)y);
            if (this.checkLocation(loc)) {
                list.add(loc);
                loc = loc.clone();
            }
        }
        return list.isEmpty() ? null : list.get(this.ran.nextInt(list.size()));
    }
    
    private int getGroundLoc(final ChunkSnapshot c, final ChunkCoord cc) {
        final ArrayList<Integer> list = new ArrayList<Integer>();
        for (int y = this.ymin; y <= this.ymax; ++y) {
            if (this.checkLocation(c, cc, y)) {
                list.add(y);
            }
        }
        return list.isEmpty() ? -1 : list.get(this.ran.nextInt(list.size()));
    }
    
    public String getPerm() {
        return this.perm;
    }
    
    public RangeFrom getRangeFrom() {
        return this.rangeFrom;
    }
    
    TeleportResult teleport(final Player p, final TeleportSource source) {
        final TeleportResult result = this.teleportPlayer(p, source);
        if (result.equals(TeleportResult.NO_LOC)) {
            this.loadLocation(this.getRandomChunkSnapshot());
            return this.teleportPlayer(p, source);
        }
        return result;
    }
    
    private TeleportResult teleportPlayer(final Player p, final TeleportSource source) {
        if (this.locs.isEmpty()) {
            return TeleportResult.NO_LOC;
        }
        final Location loc = this.locs.keySet().iterator().next();
        if (!this.checkChunk(loc.getChunk()) || !this.checkLocation(loc)) {
            this.locs.remove(this.locs.keySet().iterator().next());
            if (!this.locs.isEmpty()) {
                this.locs.keySet().iterator().next().getChunk().load();
            }
            return this.teleport(p, source);
        }
        Zone.pl.timer.addGod(p, this.invulnerability);
        this.locs.remove(loc);
        if (!this.locs.isEmpty()) {
            loc.getChunk().load();
        }
        loc.add(0.5, 0.0, 0.5);
        if (this.keepDirection) {
            loc.setYaw(p.getLocation().getYaw());
            loc.setPitch(p.getLocation().getPitch());
        }
        if (Zone.pl.zoneManager.pay(p, source)) {
            final int time = Zone.pl.getConfig().getInt("MoveRestriction.Time");
            if (Zone.pl.getConfig().getBoolean("MoveRestriction.Enable") && time > 0 && !p.hasPermission("RandomLocation.Admin") && !p.hasPermission("RandomLocation.MoveRestrictionBypass")) {
                Zone.pl.zoneManager.stillPlayers.put(p.getName(), loc);
                this.teleportTask(p, loc, p.getLocation(), time * 20);
            }
            else {
                p.teleport(loc);
                Zone.pl.zoneManager.playersTeleportToZoneLog.put(p.getUniqueId(), loc);
                if (this.kit) {
                    this.placeKit(loc);
                }
            }
            return TeleportResult.SUCCEED;
        }
        return TeleportResult.PAY_ERROR;
    }
    
    private void teleportTask(final Player p, final Location too, final Location orig, final int ticks) {
        Bukkit.getScheduler().scheduleSyncDelayedTask((Plugin)Zone.pl, (Runnable)new Runnable() {
            @Override
            public void run() {
                if (p.getLocation().getBlockX() == orig.getBlockX() && p.getLocation().getBlockY() == orig.getBlockY() && p.getLocation().getBlockZ() == orig.getBlockZ()) {
                    if (ticks > 0) {
                        Zone.this.teleportTask(p, too, orig, ticks - 1);
                    }
                    else {
                        p.teleport(too);
                        Zone.pl.zoneManager.playersTeleportToZoneLog.put(p.getUniqueId(), too);
                        if (Zone.pl.getConfig().getBoolean("MessageOnTeleport")) {
                            p.sendMessage(ChatColor.GREEN + "Teleported to zone " + Zone.this.name);
                        }
                    }
                }
                else {
                    p.sendMessage(ChatColor.GREEN + "Teleporting canceled, you need to be standing still");
                    Zone.pl.zoneManager.payBack(p);
                }
            }
        }, 1L);
    }
    
    public void placeKit(final Location loc) {
        if (this.spawnOn == SpawnOn.AIR) {
            loc.setY((double)this.getHigestBlock(loc));
        }
        final ArrayList<BlockFace> faces = new ArrayList<BlockFace>();
        loc.setX((double)loc.getBlockX());
        loc.setY((double)loc.getBlockY());
        loc.setZ((double)loc.getBlockZ());
        float yaw;
        for (yaw = loc.getYaw(), yaw %= 360.0f; yaw < 0.0f; yaw += 360.0f) {}
        BlockFace f;
        if (yaw >= 315.0f || yaw <= 45.0f) {
            faces.add(BlockFace.SOUTH);
            faces.add(BlockFace.WEST);
            faces.add(BlockFace.NORTH);
            faces.add(BlockFace.EAST);
            f = BlockFace.SOUTH;
        }
        else if (yaw >= 45.0f && yaw <= 135.0f) {
            faces.add(BlockFace.WEST);
            faces.add(BlockFace.NORTH);
            faces.add(BlockFace.EAST);
            faces.add(BlockFace.SOUTH);
            f = BlockFace.WEST;
        }
        else if (yaw >= 135.0f && yaw <= 225.0f) {
            faces.add(BlockFace.NORTH);
            faces.add(BlockFace.EAST);
            faces.add(BlockFace.SOUTH);
            faces.add(BlockFace.WEST);
            f = BlockFace.NORTH;
        }
        else {
            faces.add(BlockFace.EAST);
            faces.add(BlockFace.SOUTH);
            faces.add(BlockFace.WEST);
            faces.add(BlockFace.NORTH);
            f = BlockFace.EAST;
        }
        Block block = null;
        for (final BlockFace face : faces) {
            final Block b = loc.getBlock().getRelative(face);
            if (StringTools.arrayContains(Zone.air, b.getTypeId())) {
                block = b;
                f = face;
                break;
            }
        }
        if (block == null) {
            block = loc.getBlock();
        }
        block.setType(Material.CHEST);
        final Chest chest = (Chest)block.getState();
        final org.bukkit.material.Chest mc = (org.bukkit.material.Chest)chest.getData();
        mc.setFacingDirection(f.getOppositeFace());
        chest.setData((MaterialData)mc);
        chest.update(true);
        for (final int i : this.items.keySet()) {
            chest.getBlockInventory().setItem(i, (ItemStack)this.items.get(i));
        }
    }
    
    public boolean nonTeleportCheck(final Location loc) {
        final int x = loc.getBlockX();
        final int y = loc.getBlockY();
        final int z = loc.getBlockZ();
        final World w = Bukkit.getWorld(this.world);
        return w != null && loc.getWorld().getName().equals(this.world) && x >= this.xmin && x <= this.xmax && y >= this.ymin && y <= this.ymax && z >= this.zmin && z <= this.zmax;
    }
    
    public boolean portalCheck(final Player p) {
        final double x = p.getLocation().getX();
        final double y = p.getLocation().getY();
        final double z = p.getLocation().getZ();
        final World w = Bukkit.getWorld(this.world);
        if (w != null) {
            final double add = 0.5;
            if (x >= this.xmin - add && x <= this.xmax + add + 1.0 && y >= this.ymin - add && y <= this.ymax + add + 1.0 && z >= this.zmin - add && z <= this.zmax + add + 1.0) {
                return true;
            }
        }
        return false;
    }
    
    public boolean rangeCheck(final Player p) {
        if (this.rangeEnable) {
            final int x = p.getLocation().getBlockX();
            final int y = p.getLocation().getBlockY();
            final int z = p.getLocation().getBlockZ();
            if (this.rangeFrom.equals(RangeFrom.CUSTOM)) {
                if (this.rangeWorld != null) {
                    final World w = Bukkit.getWorld(this.rangeWorld);
                    if (w != null && x >= this.rangeX - this.range && x <= this.rangeX + this.range && y >= this.rangeY - this.range && y <= this.rangeY + this.range && z >= this.rangeZ - this.range && z <= this.rangeZ + this.range) {
                        return true;
                    }
                }
            }
            else {
                final World w = Bukkit.getWorld(this.world);
                if (w != null && x >= this.xmin - this.range && x <= this.xmax + this.range && y >= this.ymin - this.range && y <= this.ymax + this.range && z >= this.zmin - this.range && z <= this.zmax + this.range) {
                    return true;
                }
            }
            return false;
        }
        return true;
    }
    
    int getBlockID(final ChunkSnapshot c, final int x, final int y, final int z) {
        if (x > 15 || z > 15 || x < 0 || y < 0 || z < 0 || y >= this.getWorld().getMaxHeight() || c == null) {
            return 0;
        }
        return c.getBlockTypeId(x, y, z);
    }
    
    byte getBlockData(final ChunkSnapshot c, final int x, final int y, final int z) {
        if (x > 15 || z > 15 || x < 0 || y < 0 || z < 0 || y >= this.getWorld().getMaxHeight() || c == null) {
            return 0;
        }
        return (byte)c.getBlockData(x, y, z);
    }
    
    boolean checkLocation(final ChunkSnapshot c, final ChunkCoord cc, final int y) {
        final Location loc2 = this.getLocation(c, cc, y);
        if (Zone.pl.useWorldBorderHook()) {
            try {
                final BorderData bd = Config.Border(this.world);
                if (bd != null && !bd.insideBorder((double)cc.getX(), (double)cc.getZ())) {
                    return false;
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("WorldBorder");
                Zone.pl.worldBorderHook = false;
            }
        }
        if (Zone.pl.useGriefPreventionHook()) {
            try {
                final Claim claim = GriefPrevention.instance.dataStore.getClaimAt(loc2, false, (Claim)null);
                if (claim != null && !Zone.pl.ignoreGriefPrevetionPlayers.contains(claim.getOwnerName().toLowerCase())) {
                    return false;
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("GriefPrevention");
                Zone.pl.griefPreventionHook = false;
            }
        }
        if (Zone.pl.useWorldGuardHook()) {
            try {
                for (final ProtectedRegion reg : WorldGuardPlugin.inst().getRegionManager(this.getWorld()).getApplicableRegions(loc2)) {
                    if (!Zone.pl.ignoreWorldGuardRegions.contains(reg.getId().toLowerCase())) {
                        return false;
                    }
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("WorldGuard");
                Zone.pl.worldGuardHook = false;
            }
        }
        if (Zone.pl.zoneManager.insideNonTeleports(loc2)) {
            return false;
        }
        if (this.spawnOn.equals(SpawnOn.AIR)) {
            return y == 300;
        }
        if (!this.spawnOn.equals(SpawnOn.GROUND)) {
            if (this.spawnOn.equals(SpawnOn.TOP)) {
                final int standingOnId = this.getBlockID(c, cc.getX(), y - 1, cc.getZ());
                if (standingOnId == 78 && this.getBlockData(c, cc.getX(), y - 1, cc.getZ()) < 4) {
                    return false;
                }
                final int feetId = this.getBlockID(c, cc.getX(), y, cc.getZ());
                if (feetId == 78 && this.getBlockData(c, cc.getX(), y, cc.getZ()) >= 4) {
                    return false;
                }
                final int headID = this.getBlockID(c, cc.getX(), y + 1, cc.getZ());
                if (!StringTools.arrayContains(Zone.air, feetId)) {
                    return false;
                }
                if (!StringTools.arrayContains(Zone.air, headID)) {
                    return false;
                }
                if (StringTools.arrayContains(Zone.damager, standingOnId)) {
                    return false;
                }
                if (standingOnId != 78 && StringTools.arrayContains(Zone.air, standingOnId)) {
                    return false;
                }
                if (y == this.getHigestBlock(c, cc)) {
                    return true;
                }
            }
            else if (this.spawnOn.equals(SpawnOn.COMBINED)) {
                final int inId = this.getBlockID(c, cc.getX(), y, cc.getZ());
                if (inId == 78 && this.getBlockData(c, cc.getX(), y, cc.getZ()) >= 4) {
                    return false;
                }
                final int standingOnId2 = this.getBlockID(c, cc.getX(), y - 1, cc.getZ());
                final byte standingOnData = this.getBlockData(c, cc.getX(), y - 1, cc.getZ());
                if (standingOnId2 == 78 && standingOnData < 4) {
                    return false;
                }
                if (this.spawnOnBlocks.length > 0) {
                    boolean contains = false;
                    BlockData[] spawnOnBlocks;
                    for (int length = (spawnOnBlocks = this.spawnOnBlocks).length, i = 0; i < length; ++i) {
                        final BlockData bdata = spawnOnBlocks[i];
                        if (bdata.hasData(standingOnId2, standingOnData)) {
                            contains = true;
                            break;
                        }
                    }
                    if (!contains) {
                        return false;
                    }
                }
                if (!StringTools.arrayContains(Zone.air, inId)) {
                    return false;
                }
                if (!StringTools.arrayContains(Zone.air, this.getBlockID(c, cc.getX(), y + 1, cc.getZ()))) {
                    return false;
                }
                if (StringTools.arrayContains(Zone.damager, standingOnId2)) {
                    return false;
                }
                if (standingOnId2 != 78 && StringTools.arrayContains(Zone.air, standingOnId2)) {
                    return false;
                }
                if (y == this.getHigestBlock(c, cc)) {
                    return true;
                }
            }
            return false;
        }
        final int blockID = this.getBlockID(c, cc.getX(), y - 1, cc.getZ());
        final byte data = this.getBlockData(c, cc.getX(), y - 1, cc.getZ());
        if (this.spawnOnBlocks.length > 0) {
            boolean contains2 = false;
            BlockData[] spawnOnBlocks2;
            for (int length2 = (spawnOnBlocks2 = this.spawnOnBlocks).length, j = 0; j < length2; ++j) {
                final BlockData bdata2 = spawnOnBlocks2[j];
                if (bdata2.hasData(blockID, data)) {
                    contains2 = true;
                    break;
                }
            }
            if (!contains2) {
                return false;
            }
        }
        if (blockID == 78 && data < 4) {
            return false;
        }
        final int feetID = this.getBlockID(c, cc.getX(), y, cc.getZ());
        if (feetID == 78 && this.getBlockData(c, cc.getX(), y, cc.getZ()) >= 4) {
            return false;
        }
        final int headId = this.getBlockID(c, cc.getX(), y + 1, cc.getZ());
        return StringTools.arrayContains(Zone.air, feetID) && StringTools.arrayContains(Zone.air, headId) && headId != 78 && (blockID == 78 || !StringTools.arrayContains(Zone.air, blockID)) && !StringTools.arrayContains(Zone.damager, blockID);
    }
    
    boolean checkLocation(final Location loc) {
        if (Zone.pl.useWorldBorderHook()) {
            try {
                final BorderData bd = Config.Border(this.world);
                if (bd != null && !bd.insideBorder(loc)) {
                    return false;
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("WorldBorder");
                Zone.pl.worldBorderHook = false;
            }
        }
        if (Zone.pl.useGriefPreventionHook()) {
            try {
                final Claim claim = GriefPrevention.instance.dataStore.getClaimAt(loc, false, (Claim)null);
                if (claim != null && !Zone.pl.ignoreGriefPrevetionPlayers.contains(claim.getOwnerName().toLowerCase())) {
                    return false;
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("GriefPrevention");
                Zone.pl.griefPreventionHook = false;
            }
        }
        if (Zone.pl.useWorldGuardHook()) {
            try {
                for (final ProtectedRegion reg : WorldGuardPlugin.inst().getRegionManager(this.getWorld()).getApplicableRegions(loc)) {
                    if (!Zone.pl.ignoreWorldGuardRegions.contains(reg.getId().toLowerCase())) {
                        return false;
                    }
                }
            }
            catch (Exception e) {
                Zone.pl.logPluginUncompatible("WorldGuard");
                Zone.pl.worldGuardHook = false;
            }
        }
        if (Zone.pl.zoneManager.insideNonTeleports(loc)) {
            return false;
        }
        if (this.spawnOn.equals(SpawnOn.AIR)) {
            return loc.getBlockY() == 300;
        }
        if (!this.spawnOn.equals(SpawnOn.GROUND)) {
            if (this.spawnOn.equals(SpawnOn.TOP)) {
                final Block standingOn = loc.getBlock().getRelative(BlockFace.DOWN);
                final int standingOnId = standingOn.getTypeId();
                if (standingOnId == 78 && standingOn.getData() < 4) {
                    return false;
                }
                final Block feet = loc.getBlock();
                final int feetId = feet.getTypeId();
                if (feetId == 78 && feet.getData() >= 4) {
                    return false;
                }
                if (!StringTools.arrayContains(Zone.air, feetId)) {
                    return false;
                }
                if (!StringTools.arrayContains(Zone.air, feet.getRelative(BlockFace.UP).getTypeId())) {
                    return false;
                }
                if (StringTools.arrayContains(Zone.damager, standingOnId)) {
                    return false;
                }
                if (standingOnId != 78 && StringTools.arrayContains(Zone.air, standingOnId)) {
                    return false;
                }
                if (loc.getY() == this.getHigestBlock(loc)) {
                    return true;
                }
            }
            else if (this.spawnOn.equals(SpawnOn.COMBINED)) {
                final Block in = loc.getBlock();
                final int inId = in.getTypeId();
                if (inId == 78 && in.getData() >= 4) {
                    return false;
                }
                final Block standingOn2 = in.getRelative(BlockFace.DOWN);
                final int standingOnId2 = standingOn2.getTypeId();
                if (standingOnId2 == 78 && standingOn2.getData() < 4) {
                    return false;
                }
                final byte data = standingOn2.getData();
                if (this.spawnOnBlocks.length > 0) {
                    boolean contains = false;
                    BlockData[] spawnOnBlocks;
                    for (int length = (spawnOnBlocks = this.spawnOnBlocks).length, i = 0; i < length; ++i) {
                        final BlockData bdata = spawnOnBlocks[i];
                        if (bdata.hasData(standingOnId2, data)) {
                            contains = true;
                            break;
                        }
                    }
                    if (!contains) {
                        return false;
                    }
                }
                final Block head = in.getRelative(BlockFace.UP);
                if (!StringTools.arrayContains(Zone.air, inId)) {
                    return false;
                }
                if (!StringTools.arrayContains(Zone.air, head.getTypeId())) {
                    return false;
                }
                if (StringTools.arrayContains(Zone.damager, standingOnId2)) {
                    return false;
                }
                if (standingOnId2 != 78 && StringTools.arrayContains(Zone.air, standingOn2)) {
                    return false;
                }
                if (loc.getY() == this.getHigestBlock(loc)) {
                    return true;
                }
            }
            return false;
        }
        final Block locblock = loc.getBlock();
        final Block block = locblock.getRelative(BlockFace.DOWN);
        final int blockID = block.getTypeId();
        final byte data2 = block.getData();
        if (this.spawnOnBlocks.length > 0) {
            boolean contains2 = false;
            BlockData[] spawnOnBlocks2;
            for (int length2 = (spawnOnBlocks2 = this.spawnOnBlocks).length, j = 0; j < length2; ++j) {
                final BlockData bdata2 = spawnOnBlocks2[j];
                if (bdata2.hasData(blockID, data2)) {
                    contains2 = true;
                    break;
                }
            }
            if (!contains2) {
                return false;
            }
        }
        if (blockID == 78 && loc.getBlock().getRelative(BlockFace.DOWN).getData() < 4) {
            return false;
        }
        final int feetID = locblock.getTypeId();
        if (feetID == 78 && loc.getBlock().getData() >= 4) {
            return false;
        }
        final Block head = locblock.getRelative(BlockFace.UP);
        final int headId = head.getTypeId();
        return StringTools.arrayContains(Zone.air, feetID) && StringTools.arrayContains(Zone.air, headId) && headId != 78 && (blockID == 78 || !StringTools.arrayContains(Zone.air, blockID)) && !StringTools.arrayContains(Zone.damager, blockID);
    }
    
    public void setCustomRangeLocation(final Location loc) {
        this.rangeWorld = loc.getWorld().getName();
        this.rangeX = loc.getBlockX();
        this.rangeY = loc.getBlockY();
        this.rangeZ = loc.getBlockZ();
        Zone.pl.zoneManager.save(this);
    }
    
    private int getHigestBlock(final ChunkSnapshot c, final ChunkCoord cc) {
        for (int y = 255; y >= 0; --y) {
            final int id = this.getBlockID(c, cc.getX(), y, cc.getZ());
            final int data = this.getBlockData(c, cc.getX(), y, cc.getZ());
            boolean contains = false;
            if (this.topBlocks.length > 0) {
                BlockData[] topBlocks;
                for (int length = (topBlocks = this.topBlocks).length, i = 0; i < length; ++i) {
                    final BlockData bd = topBlocks[i];
                    if (bd.hasData(id, (byte)data)) {
                        contains = true;
                        break;
                    }
                }
            }
            else {
                contains = true;
            }
            if (!StringTools.arrayContains(Zone.air, id) && !contains) {
                if (id != 78) {
                    return y + 1;
                }
                if (data <= 4) {
                    return y + 1;
                }
            }
        }
        return -1;
    }
    
    private int getHigestBlock(Location loc) {
        loc = loc.clone();
        for (int y = 255; y >= 0; --y) {
            loc.setY((double)y);
            final int id = loc.getBlock().getTypeId();
            boolean contains = false;
            if (this.topBlocks.length > 0) {
                BlockData[] topBlocks;
                for (int length = (topBlocks = this.topBlocks).length, i = 0; i < length; ++i) {
                    final BlockData data = topBlocks[i];
                    if (data.hasData(loc)) {
                        contains = true;
                        break;
                    }
                }
            }
            else {
                contains = true;
            }
            if (!StringTools.arrayContains(Zone.air, id) && !contains) {
                if (id != 78) {
                    return y + 1;
                }
                if (loc.getBlock().getData() <= 4) {
                    return y + 1;
                }
            }
        }
        return -1;
    }
    
    @Override
    public String toString() {
        String items = "";
        for (final int i : this.items.keySet()) {
            final ItemStack item = this.items.get(i);
            if (item != null) {
                items = String.valueOf(items) + "-" + i + "=" + StringTools.itemToString(item);
            }
        }
        items = items.replaceFirst("-", "");
        final Object[] list = { this.name, this.perm, this.rangeEnable, this.range, this.rangeFrom, this.keepDirection, this.checks, this.spawnOn, StringTools.arrayToString(this.spawnOnBlocks, "-"), StringTools.arrayToString(this.topBlocks, "-"), this.cmdCooldown, this.signsCooldown, this.portalCooldown, this.invulnerability, this.x1, this.y1, this.z1, this.x2, this.y2, this.z2, this.world, this.rangeX, this.rangeY, this.rangeZ, this.rangeWorld, this.type.getShortString(), this.portalDest, this.kit, items };
        return StringTools.arrayToString(list, ",");
    }
    
    public void loadFromString(final String settings) {
        final String[] args = settings.split(",");
        Zone.pl.getLogger().info("Loading zone: " + args[0]);
        try {
            this.name = args[0];
            this.perm = args[1];
            this.rangeEnable = StringTools.getBoolean(args[2]);
            if (StringTools.isInt(args[3])) {
                this.range = StringTools.getInteger(args[3]);
            }
            this.rangeFrom = RangeFrom.get(args[4]);
            if (StringTools.isBoolean(args[5])) {
                this.keepDirection = StringTools.getBoolean(args[5]);
            }
            if (StringTools.isInt(args[6])) {
                this.checks = StringTools.getInteger(args[6]);
            }
            this.spawnOn = SpawnOn.get(args[7]);
            if (StringTools.isIntArray(args[8], "-")) {
                this.spawnOnBlocks = StringTools.getBlockDataArray(args[8], "-");
            }
            if (StringTools.isIntArray(args[9], "-")) {
                this.topBlocks = StringTools.getBlockDataArray(args[9], "-");
            }
            if (StringTools.isInt(args[10])) {
                this.cmdCooldown = StringTools.getInteger(args[10]);
            }
            if (StringTools.isInt(args[11])) {
                this.signsCooldown = StringTools.getInteger(args[11]);
            }
            if (StringTools.isInt(args[12])) {
                this.portalCooldown = StringTools.getInteger(args[12]);
            }
            if (StringTools.isInt(args[13])) {
                this.invulnerability = StringTools.getInteger(args[13]);
            }
            this.x1 = StringTools.getInteger(args[14]);
            this.y1 = StringTools.getInteger(args[15]);
            this.z1 = StringTools.getInteger(args[16]);
            this.x2 = StringTools.getInteger(args[17]);
            this.y2 = StringTools.getInteger(args[18]);
            this.z2 = StringTools.getInteger(args[19]);
            this.world = args[20];
            this.calculatePitch();
            this.rangeX = StringTools.getInteger(args[21]);
            this.rangeY = StringTools.getInteger(args[22]);
            this.rangeZ = StringTools.getInteger(args[23]);
            this.rangeWorld = args[24];
            this.type = ZoneType.getByString(args[25]);
            if (this.type == null) {
                this.type = ZoneType.TELEPORT;
            }
            this.portalDest = args[26];
            if (StringTools.isBoolean(args[27])) {
                this.kit = StringTools.getBoolean(args[27]);
            }
            String[] split;
            for (int length = (split = args[28].split("-")).length, i = 0; i < length; ++i) {
                final String key = split[i];
                try {
                    this.items.put(Integer.parseInt(key.split("=")[0]), StringTools.getItemStack(key.replaceFirst(String.valueOf(key.split("=")[0]) + "=", "")));
                }
                catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        catch (ArrayIndexOutOfBoundsException e2) {
            if (!settings.endsWith(",")) {
                Zone.pl.getLogger().info("  -Wrong or old formatting found for zone " + this.name + ", trying to fix it...");
            }
        }
    }
    
    public void calculatePitch() {
        this.xmax = this.getMax(this.x1, this.x2);
        this.ymax = this.getMax(this.y1, this.y2);
        this.zmax = this.getMax(this.z1, this.z2);
        this.xmin = this.getMin(this.x1, this.x2);
        this.ymin = this.getMin(this.y1, this.y2);
        this.zmin = this.getMin(this.z1, this.z2);
    }
    
    public void setType(final ZoneType type) {
        this.type = type;
        Zone.pl.zoneManager.save(this);
    }
    
    public ZoneType getType() {
        return this.type;
    }
    
    public void setPortalDest(final String zone) {
        this.portalDest = zone;
        Zone.pl.zoneManager.save(this);
    }
    
    public String getInfo() {
        final String dg = new StringBuilder().append(ChatColor.DARK_GREEN).toString();
        final String g = new StringBuilder().append(ChatColor.GOLD).toString();
        String info = String.valueOf(dg) + "============ " + this.name + " ============\n" + "Type: " + g + this.type.getNormalString() + "\n";
        if (this.type.equals(ZoneType.PORTAL)) {
            info = String.valueOf(info) + dg + "Portal Destination: " + g + this.portalDest;
        }
        else if (this.type.equals(ZoneType.TELEPORT)) {
            info = String.valueOf(info) + dg + "Perm: " + g + this.perm + "\n" + dg + "UseRange: " + g + this.rangeEnable + "\n" + dg + " -Range: " + g + this.range + "\n" + dg + " -RangeFrom: " + g + this.rangeFrom.getNormalString() + "\n" + dg + "KeepDirection: " + g + this.keepDirection + "\n" + dg + "Mode: " + g + this.spawnOn.getNormalString() + "\n" + dg + " -SpawnOnBlocks: " + g + Arrays.toString(this.spawnOnBlocks) + "\n" + dg + " -TopBlocks: " + g + Arrays.toString(this.topBlocks) + "\n" + dg + "Invulnerability: " + g + this.invulnerability + "s\n" + dg + "UseKit: " + g + this.kit + "\n" + dg + "Kit: " + g + "(Use /rLoc option " + this.name + " setKit)";
        }
        info = String.valueOf(info) + "\n" + dg + "World: " + g + this.world;
        info = String.valueOf(info) + "\n" + dg + "Pos1: " + g + this.getStringPos(true);
        info = String.valueOf(info) + "\n" + dg + "Pos2: " + g + this.getStringPos(false);
        return info;
    }
    
    private String getStringPos(final boolean first) {
        if (first) {
            return "(" + this.x1 + "," + this.y1 + "," + this.z1 + ")";
        }
        return "(" + this.x2 + "," + this.y2 + "," + this.z2 + ")";
    }
    
    public void setPerm(final String perm) {
        this.perm = perm;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setUseRange(final boolean flag) {
        this.rangeEnable = flag;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setRange(final int range) {
        this.range = range;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setRangeFrom(final RangeFrom from) {
        this.rangeFrom = from;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setKeepDirection(final boolean flag) {
        this.keepDirection = flag;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setMode(final SpawnOn spawnOn) {
        this.spawnOn = spawnOn;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setGroundBlocks(final BlockData[] blocks) {
        this.spawnOnBlocks = blocks;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setTopBlocks(final BlockData[] blocks) {
        this.topBlocks = blocks;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setInvulnerability(final int seconds) {
        this.invulnerability = seconds;
        Zone.pl.zoneManager.save(this);
    }
    
    public World getWorld() {
        return Bukkit.getWorld(this.world);
    }
    
    public String getPortalDest() {
        return this.portalDest;
    }
    
    public void setUseKit(final boolean b) {
        this.kit = b;
        Zone.pl.zoneManager.save(this);
    }
    
    public void setItems(final HashMap<Integer, ItemStack> items) {
        this.items = items;
        Zone.pl.zoneManager.save(this);
    }
    
    public Inventory getKit() {
        final String name = "Zone Starter Kit";
        final Inventory inv = Bukkit.createInventory((InventoryHolder)null, 27, name);
        for (final int i : this.items.keySet()) {
            inv.setItem(i, (ItemStack)this.items.get(i));
        }
        return inv;
    }
    
    Location getNextLoc() {
        if (this.locs.isEmpty()) {
            return null;
        }
        return this.locs.keySet().iterator().next().clone();
    }
}
